#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os

import cherrypy
from cherrypy.lib.static import serve_file

from farado.config import global_config, application_config, farado_config
from farado.logger import logger
from farado.ui.users_view import UsersView
from farado.ui.projects_view import ProjectsView
from farado.ui.boards_view import BoardsView
from farado.ui.roles_view import RolesView
from farado.ui.workflows_view import WorkflowsView
from farado.ui.issue_kinds_view import IssueKindsView
from farado.ui.issues_view import IssuesView
from farado.ui.search_view import SearchView
from farado.ui.renderer import view_renderer
from farado.ui.operation_result import OperationResult
from farado.helpers.cookie_helper import current_session_id, set_current_session_id
from farado.general_manager_holder import project_manager, permission_manager
from farado.ui.base_view import BaseView, UiUserRestrictions, get_value
from farado.editors.issue_editor import IssueEditor
from farado.hoox.active_hoox import hoox

class WebService(BaseView):

    #--------------------------------------------------------------------------#
    def __init__(self):
        super().__init__()
        self.name = '/'

        def subscribe_view(view):
            view.process_function = self.process_function
            return view

        self.users_view       = subscribe_view(UsersView())
        self.projects_view    = subscribe_view(ProjectsView())
        self.boards_view      = subscribe_view(BoardsView())
        self.roles_view       = subscribe_view(RolesView())
        self.workflows_view   = subscribe_view(WorkflowsView())
        self.issue_kinds_view = subscribe_view(IssueKindsView())
        self.issues_view      = subscribe_view(IssuesView())
        self.search_view      = subscribe_view(SearchView())

    #--------------------------------------------------------------------------#
    def process_function(self, user, function):
        if not user:
            logger.warning('No user')
            return (False, 'No user')

        rights = self.project_rights(user.id)

        # function example '/issues/issue/12?issue_state_id=3'
        data = function.split('?')
        path = data[0]
        args = None
        if len(data) > 1:
            args = data[1]

        path = path.split('/')
        id = path[-1]
        target_item = path[-2]
        if 'issue' == target_item:
            return IssueEditor(user).change(rights, issue_id=id, **args_to_dict(args))

        logger.warning('No function')
        return (False, 'No function')

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def hook(self, *args_tuple, **args_dict):
        result = ''
        if cherrypy.request.process_request_body:
            request_headers = cherrypy.request.headers
            request_body = cherrypy.request.body.read()

        for hook_type in hoox:
            hook = hook_type(
                args_tuple=args_tuple,
                args_dict=args_dict,
                request_headers=request_headers,
                request_body=request_body,
            )
            if hook.is_active() and hook.proceed():
                result = hook.result()
                break

        return result

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def index(self, login=None, password=None, function=None, **args):
        if login:
            session_id = permission_manager().login(login, password)
        else:
            session_id = current_session_id()

        user = permission_manager().user_by_session_id(session_id)
        if not user:
            return view_renderer["login"].render()

        set_current_session_id(session_id)

        # Принудительная смена пароля
        if user.need_change_password:
            if not args:
                return view_renderer["need_change_password"].render(user=user)
            user.set_password(get_value(args, 'new_password', None))
            user.need_change_password = False
            project_manager().save_item(user)
            user.push_last_action_result(
                OperationResult(
                    caption="Пароль сохранён",
                    kind="success"
                )
            )
            logger.info(f"%-18s | Пользователь сменил свой пароль.", user.login)

        # Если сессия пользователя закончилась и он выполнил какое либо действие
        # система сохраняет информацию о данном действии (function) и перечень
        # параметров действия (args). После удачного входа в систему
        # пользователя перенаправляем к нужной функции.
        if function:
            user.push_last_action_data(args)
            raise cherrypy.HTTPRedirect(cherrypy.url(function))

        # TODO: Временно добавил перенаправление на страницу статистики
        #       текущего пользователя.
        raise cherrypy.HTTPRedirect(
            cherrypy.url(f'/users/user_statistics/{ user.id }')
        )
        return view_renderer["index"].render(
            user=user,
            project_manager=project_manager(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                )
            )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def logout(self):
        permission_manager().logout(current_session_id())
        set_current_session_id(None)
        return view_renderer["login"].render()

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def logs(self):
        user = self.current_user()
        if not user:
            return self.login_and_open('/logs')

        is_admin = self.is_admin(user.id)
        if not is_admin:
            message = f"Нет доступа для просмотра пользовательских логов."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        # TODO: read filename from single point
        log_filename = 'resources/logs/farado.log'
        with open(log_filename, 'r') as file:
            logs_data = file.read()

        message = f"Открытие страницы с перечнем действий пользователей."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["logs"].render(
            user=user,
            log_data=logs_data,
            project_manager=project_manager(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
            )
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def database(self):
        user = self.current_user()
        if not user:
            return self.login_and_open('/database')

        is_admin = self.is_admin(user.id)
        if not is_admin:
            message = f"Нет доступа к базе данных."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        connection_string = farado_config['database']['connection_string']
        if not connection_string.startswith('sqlite://'):
            message = f"Нет доступа к базе данных."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        abs_database_path = os.path.abspath(
            connection_string.replace('sqlite://', '.'))

        return serve_file(
            abs_database_path,
            "application/x-download",
            "attachment",
            "database.sqlite")

    #--------------------------------------------------------------------------#
    def error_page_403(self, **args):
        return view_renderer["403"].render(
            project_manager=project_manager(),
            traceback=get_value(args, 'traceback', None),
            message=get_value(args, 'message', None),
            status=get_value(args, 'status', None),
            version=get_value(args, 'version', None),
        )

    #--------------------------------------------------------------------------#
    def error_page_404(self, **args):
        message=get_value(args, 'message', None)

        # HACK: замена сообщений об ошибочном пути на русский, лучше это делать
        #       не здесь
        if 'was not found' in message:
            message_data = message.split("'")
            if len(message_data) > 2:
                message = f"Путь '{message_data[1]}' не найден."
            else:
                message = f"Путь не найден."

        return view_renderer["404"].render(
            project_manager=project_manager(),
            traceback=get_value(args, 'traceback', None),
            message=message,
            status=get_value(args, 'status', None),
            version=get_value(args, 'version', None),
        )

    #--------------------------------------------------------------------------#
    def run(self):
        # HACK: switching off date time output for cherrypy log
        cherrypy._cplogging.LogManager.time = lambda self : "cherrypy"

        cherrypy.config.update(global_config)
        cherrypy.config.update({ 'error_page.403': self.error_page_403 })
        cherrypy.config.update({ 'error_page.404': self.error_page_404 })
        cherrypy.log.screen = False

        def mount_view(view, config):
            cherrypy.tree.mount(view, view.name, config)

        mount_view(self.users_view, application_config)
        mount_view(self.projects_view, application_config)
        mount_view(self.boards_view, application_config)
        mount_view(self.roles_view, application_config)
        mount_view(self.workflows_view, application_config)
        mount_view(self.issue_kinds_view, application_config)
        mount_view(self.issues_view, application_config)
        mount_view(self.search_view, application_config)
        cherrypy.quickstart(self, self.name, application_config)


#------------------------------------------------------------------------------#
def args_to_dict(args):
    '''Splits string of attributes to the dictionary

    Parameters
    ----------
    args : String
        String with attributes separated by ampersand 'state_id=3&kind_value=2'

    Returns
    -------
    Dict
        {'state_id': '3', 'kind_value': '2'}
    '''
    if args.startswith('\''):
        args = args[1:]
    if args.endswith('\''):
        args = args[:-1]

    result = {}
    for arg in args.split('&'):
        key_value = arg.split('=')
        key = key_value[0]
        value = key_value[1]
        result[key] = value
    return result
